﻿//-------------------------------------------------------------------------------------------------
// <copyright file="Columns.cs" company="Oliver Hallam">
// Copyright (c) 2008 Oliver Hallam
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// </copyright>
//-------------------------------------------------------------------------------------------------

namespace DBLite
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Diagnostics.Contracts;
    using Contracts;

    /// <summary>
    /// The columns in a <see cref="Table"/>.
    /// </summary>
    [Pure]
    public class Columns
    {
        #region Fields

        /// <summary>
        /// The column definitions.
        /// </summary>
        private readonly Column[] columns;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Columns"/> class.
        /// </summary>
        /// <param name="columns">The columns.</param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="columns"/> is <see langword="null" />.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// One of the values in <paramref name="columns"/> is <see langword="null" />
        /// </exception>
        public Columns(params Column[] columns)
        {
            Ensure.ArgumentNotNull(columns, "columns");

            this.columns = CopyColumns(columns);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the number of columns.
        /// </summary>
        /// <value>The number of columns.</value>
        public int Count
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);

                return this.columns.Length;
            }
        }

        /// <summary>
        /// Gets the <see cref="Column"/> with the specified ID.
        /// </summary>
        /// <param name="id">The ID of the column to retrieve.</param>
        /// <value>The column with the specified ID.</value>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="id"/> is out of range</exception>
        public Column this[int id]
        {
            get
            {
                Ensure.ArgumentInRange(id, this.columns.Length, "id");

                return this.columns[id];
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets the ID of a particular column.
        /// </summary>
        /// <param name="name">The name of the column to lookup.</param>
        /// <returns>
        /// The ID of the column, or -1 if there is no column with matching name.
        /// </returns>
        public int GetColumnId(string name)
        {
            Ensure.ArgumentNotNull(name, "name");

            for (int i = 0; i < this.columns.Length; i++)
            {
                Column column = this.columns[i];

                Contract.Assume(column != null);

                if (column.Name == name)
                {
                    return i;
                }
            }

            return -1;
        }

        /// <summary>
        /// Object invariants.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Code contract")]
        [ContractInvariantMethod]
        protected void ObjectInvariants()
        {
            Contract.Invariant(this.columns != null);
            ////Contract.Invariant(Contract.ForAll(this.columns, column => column != null));
        }

        /// <summary>
        /// Copies an array of columns and ensures none of them are null.
        /// </summary>
        /// <param name="columns">The columns.</param>
        /// <returns>A copy of <paramref name="columns"/>.</returns>
        /// <exception cref="ArgumentException">One of the columns was <see langword="null" />.</exception>
        private static Column[] CopyColumns(Column[] columns)
        {
            Contract.Requires(columns != null);
            Contract.Ensures(Contract.Result<Column[]>() != null);

            int length = columns.Length;
            var newColumns = new Column[length];

            for (int i = 0; i < newColumns.Length; i++)
            {
                Column column = columns[i];

                if (column == null)
                {
                    throw new ArgumentException(ExceptionResources.ColumnNull());
                }

                newColumns[i] = column;
            }

            return newColumns;
        }

        #endregion
    }
}